package pers.wwz.study.file.utils;

import lombok.extern.log4j.Log4j2;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.info.MultimediaInfo;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Log4j2
public class FfmpegUtils {

    /**
     * 分割长度(单位:秒)
     */
    public final static String SPLIT_LEN = "9";

    /**
     * 舍弃的开头长度(单位:秒)
     */
    public final static Long DROP_HEAD = 30L;


    /**
     * 舍弃的结尾长度(单位:秒)
     */
    public final static Long DROP_END = 30L;


    public static void main(String[] args) throws IOException, EncoderException {

        //String path ="D:\\BaiduNetdiskDownload\\So-VITS-SVC新版\\新版整合包\\so-vits-svc\\so-vits-svc\\dataset_raw\\zhoushen\\";

        // 测试执行cmd
        //String command = "ffmpeg -y -i C:\\Users\\Mayn\\Desktop\\tmp\\yangzongwei\\1.wav -f segment -segment_time 50 C:\\Users\\Mayn\\Desktop\\tmp\\yangzongwei\\1_split%d.wav";

        //runtimeExecuteCmd(command);
        //processBuilderexecuteCmd(command);


        String path = "C:\\Users\\Mayn\\Desktop\\tmp\\zhoushen\\";

        // 1.重命名原始文件
        reName(path);

        // 2.生成并执行截取文件脚本然后删除原文件
        doGenAndExecSubFileCommandAndThenDelOriginalFile(path, DROP_HEAD, DROP_END);


        // 3.生成分割shell脚本然后删除原文件
        doGenAndExecSplitShellAndThenDelOriginalFileCmd(path);




    }

    /**
     * Java ProcessBuilder调用CMD命令
     * 这个 '"cmd.exe", "/c"'不能省掉,否则会报错
     * 建议使用这种方式
     *
     * @param command 命令
     */
    public static void processBuilderexecuteCmd(String command) {
        try {
            // 调用CMD命令
            ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", command); // /c参数表示执行后关闭CMD窗口
            processBuilder.redirectErrorStream(true); // 将错误输出流与标准输出流合并
            Process process = processBuilder.start();

            // 获取命令输出结果
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); // 设置编码为GBK
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待命令执行完成
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Java Runtime调用CMD命令
     *
     * @param command 命令
     */
    public static void runtimeExecuteCmd(String command) {
        try {
            // 调用CMD命令
            Process process = Runtime.getRuntime().exec(command);

            // 获取命令输出结果
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); // 设置编码为GBK
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待命令执行完成
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

    }

    /**
     * 生成截取文件脚本
     *
     * @param pathTmp pathTmp
     * @param subHead 文件舍弃开头时间长度(单位:秒)
     * @param subEnd  文件舍弃捷微时间长度(单位:秒)
     * @return 命令
     * @throws EncoderException
     */
    public static String doGenExecSubFileCommand(Path pathTmp, Long subHead, Long subEnd) throws EncoderException {

        String filePath = pathTmp.toString();

        File file = pathTmp.toFile();

        String fileName = file.getName();

        // 1.获取文件时长
        Long fileDuration = getFileDuration(filePath);

        // 2.截取后的文件时长
        Long subFileDuration = fileDuration - subHead - subEnd;

        String endStr = "";
        if (subFileDuration < 0) {
            log.error("截取后文件时长小于0,不支持截取");
        } else {
            endStr = formatFileDuration(subFileDuration);
        }
        String startStr = "00:00:" + subHead;


        String subCommandStr = String.format("ffmpeg.exe -i %s -ss %s -t %s %s", filePath, startStr, endStr, filePath.split("\\.")[0] + "sub_.wav");
        log.info(subCommandStr);

        return subCommandStr;


    }


    /**
     * 生成并执行 截取文件脚本
     * @param path 文件路径
     * @param dropHead   舍弃的开头长度(单位:秒)
     * @param dropEnd    舍弃的结尾长度(单位:秒)
     * @return 结果
     * @throws EncoderException
     */
    public static Set<String> doGenAndExecSubFileCommandAndThenDelOriginalFile(String path, Long dropHead, Long dropEnd) throws EncoderException {

        HashSet<String> set = new HashSet<>();
        try (
                Stream<Path> paths = Files.walk(Paths.get(path))) {
            List<Path> fileNames = paths
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toList());

            for (int i = 0; i < fileNames.size(); i++) {
                Path pathTmp = fileNames.get(i);
                String filePath = pathTmp.toString();
                File file = pathTmp.toFile();

                String fileName = file.getName();

                // 1.获取文件时长
                Long fileDuration = getFileDuration(filePath);

                // 2.截取后的文件时长
                Long subFileDuration = fileDuration - dropHead - dropEnd;

                String endStr = "";
                if (subFileDuration < 0) {
                    log.error("截取后文件时长小于0,不支持截取");
                } else {
                    endStr = formatFileDuration(subFileDuration);
                }
                String startStr = "";
                if(dropHead<10){
                    startStr = "00:00:0" + dropHead;
                }else {
                    startStr = "00:00:" + dropHead;
                }


                String subCommandStr = String.format("ffmpeg.exe -i %s -ss %s -t %s %s", filePath, startStr, endStr, filePath.split("\\.")[0] + "sub_.wav");
                log.info("生成并执行截取文件脚本:{}",subCommandStr);
                // 3.执行脚本
                processBuilderexecuteCmd(subCommandStr);


                // 4.删除原文件
                if(file.exists() && file.isFile()){
                    file.delete();
                }

                set.add(subCommandStr);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return set;


    }

    /**
     * 生成截取文件脚本
     * @param path 文件路径
     * @param dropHead   舍弃的开头长度(单位:秒)
     * @param dropEnd    舍弃的结尾长度(单位:秒)
     * @return 结果
     * @throws EncoderException
     */
    public static Set<String> doGenSubFileCommand(String path, Long dropHead, Long dropEnd) throws EncoderException {

        HashSet<String> set = new HashSet<>();
        try (
                Stream<Path> paths = Files.walk(Paths.get(path))) {
            List<Path> fileNames = paths
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toList());

            for (int i = 0; i < fileNames.size(); i++) {
                Path pathTmp = fileNames.get(i);
                String filePath = pathTmp.toString();
                File file = pathTmp.toFile();

                String fileName = file.getName();

                // 1.获取文件时长
                Long fileDuration = getFileDuration(filePath);

                // 2.截取后的文件时长
                Long subFileDuration = fileDuration - dropHead - dropEnd;

                String endStr = "";
                String startStr = "";
                if (subFileDuration < 0) {
                    log.error("截取后文件时长小于0,不支持截取");
                } else {
                    endStr = formatFileDuration(subFileDuration);
                }
                if(dropHead<10){
                    startStr = "00:00:0" + dropHead;
                }else {
                    startStr = "00:00:" + dropHead;
                }


                String subCommandStr = String.format("ffmpeg.exe -i %s -ss %s -t %s %s", filePath, startStr, endStr, filePath.split("\\.")[0] + "sub_.wav");
                set.add(subCommandStr);
                log.info(subCommandStr);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return set;


    }

    /**
     * 根据文件时长格式化时长(结果格式时分秒,示例:00:00:00)
     *
     * @param fileDuration 文件时长
     * @return 结果
     */
    public static String formatFileDuration(Long fileDuration) {
        // 小时
        long hour = fileDuration / 3600;

        // 分钟
        long minute = fileDuration / 60;

        // 秒
        long second = fileDuration % 60;

        //log.info(fileDuration);

        log.info("hour:" + hour);

        log.info("minute:" + minute);

        log.info("second:" + second);

        StringBuffer stringBuffer = new StringBuffer("");

        if (hour >= 0 && hour < 10) {
            stringBuffer.append("0").append(hour).append(":");
        } else if (hour > 10 && hour <= 99) {
            stringBuffer.append(hour).append(":");
        }

        if (minute >= 0 && minute < 10) {
            stringBuffer.append("0").append(minute).append(":");
        } else if (hour > 10 && hour <= 60) {
            stringBuffer.append(minute).append(":");
        }


        if (second >= 0 && second < 10) {
            stringBuffer.append("0").append(hour);
        } else if (second > 10 && second <= 60) {
            stringBuffer.append(second);
        }


        return stringBuffer.toString();
    }

    /**
     * 获取文件时长 支持视频和音频
     *
     * @param filepath 文件路径
     * @return 时长
     * @throws EncoderException
     */
    public static Long getFileDuration(String filepath) throws EncoderException {
        File file = new File(filepath);
        MultimediaObject instance = new MultimediaObject(file);
        MultimediaInfo result = instance.getInfo();
        return result.getDuration() / 1000;
    }


    /**
     * 重命名文件
     *
     * @param path 文件夹目录
     */
    public static void reName(String path) {
        // 路径
        try (
                Stream<Path> paths = Files.walk(Paths.get(path))) {
            List<Path> fileNames = paths
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toList());

            for (int i = 0; i < fileNames.size(); i++) {
                Path pathTmp = fileNames.get(i);
                Path fileName = pathTmp.getFileName();
                String fileNameOld = fileName.toString();
                String fileileNameNew = i + ".wav";


                File oldFile = new File(path + fileNameOld);
                File newFile = new File(path + fileileNameNew);
                if (oldFile.renameTo(newFile)) {
                    log.info("文件重命名成功！");
                } else {
                    log.info("文件重命名失败！");
                }

                log.info(fileNameOld);
                log.info(fileileNameNew);

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成分割脚本
     *
     * @param pathTmp
     * @return  分割脚本
     */
    public static String doGenSplitShellCmd(Path pathTmp) {

        Path fileName = pathTmp.getFileName();
        String fileNameOld = fileName.toString();

        String sbuCommandStr = String.format("ffmpeg -i %s -f segment -segment_time %s %s_", fileNameOld,SPLIT_LEN, fileNameOld.replace(".wav","_split"));
        sbuCommandStr = sbuCommandStr + "%03d.wav";
        log.info(sbuCommandStr);

        return sbuCommandStr;

    }

    /**
     * 生成并执行shell分割脚本
     * @param path 文件路径
     */
    public static void doGenAndExecSplitShellAndThenDelOriginalFileCmd(String path) {

        try (
                Stream<Path> paths = Files.walk(Paths.get(path))) {
            List<Path> fileNames = paths
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toList());

            for (int i = 0; i < fileNames.size(); i++) {
                Path pathTmp = fileNames.get(i);
                Path fileName = pathTmp.getFileName();
                String fileNameOld = fileName.toString();

                String command = String.format("ffmpeg -i %s -f segment -segment_time %s %s_", path + fileNameOld, SPLIT_LEN,path + fileNameOld.replace(".wav", "_split"));
                command = command + "%03d.wav";
                log.info(command);
                processBuilderexecuteCmd(command);
                File file = pathTmp.toFile();
                // 删除原文件
                if(file.exists() && file.isFile()){
                    file.delete();
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成shell分割脚本
     * @param path 文件路径
     */
    public static void doGenSplitShellCmd(String path) {

        try (
                Stream<Path> paths = Files.walk(Paths.get(path))) {
            List<Path> fileNames = paths
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toList());

            for (int i = 0; i < fileNames.size(); i++) {
                Path pathTmp = fileNames.get(i);
                Path fileName = pathTmp.getFileName();
                String fileNameOld = fileName.toString();

                String command = String.format("ffmpeg -i %s -f segment -segment_time %s %s_", path + fileNameOld, SPLIT_LEN, path + fileNameOld.replace(".wav", "_split"));
                command = command + "%03d.wav";
                log.info(command);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * ffmpeg获取音频时长
     *
     * @return //TODO 不能用
     */
    public static String getAudioTimeLength(File file) {
        StringBuffer ffmpeg_shell = new StringBuffer();
        ffmpeg_shell.append("ffmpeg");
        ffmpeg_shell.append(" ");
        ffmpeg_shell.append("-i " + file.getPath());
        ffmpeg_shell.append(" ");
        ffmpeg_shell.append("2$amp;");
        ffmpeg_shell.append(">amp;");
        ffmpeg_shell.append("$amp;");
        ffmpeg_shell.append("1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//");

        System.out.println("执行的命令集合  " + ffmpeg_shell.toString());

        String os = System.getProperty("os.name");
        String shell[];
        if (os.toLowerCase().startsWith("win")) {
            shell = new String[]{"cmd", ffmpeg_shell.toString()};
        } else {
            shell = new String[]{"/bin/sh", "-c", ffmpeg_shell.toString()};
        }
        // 报错,先不管,先用别的方案
        //return  ShellExcuter.exec(shell);
        return null;
    }

    /**
     * 格式化音频时长 00:00:07.76
     * //TODO 不能用
     */
    public static String formatDurationTime(String duration) {
        try {
            String audioTime = duration.replace(".", ":").substring(duration.indexOf(":") + 1);
            String time_hh = audioTime.substring(audioTime.lastIndexOf(":") - 5).split(":")[0]; //时

            String time_mm = audioTime.substring(audioTime.lastIndexOf(":") - 2).split(":")[0]; //分
            String time_ss = audioTime.substring(audioTime.lastIndexOf(":") + 1); //秒
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.HOUR, Integer.parseInt(time_hh));
            calendar.set(Calendar.MINUTE, Integer.parseInt(time_mm));
            calendar.set(Calendar.SECOND, Integer.parseInt(time_ss));
            SimpleDateFormat format = new SimpleDateFormat("hh:mm");
            String retunTime = format.format(calendar.getTime());
            if (time_hh.contains("00")) {
                retunTime = retunTime.replace("12", "00");
            }
            Integer sub_ss = Integer.parseInt(duration.substring(duration.lastIndexOf(".")).replace(".", ""));
            if (sub_ss > 60) {
                sub_ss = sub_ss % 60;
            }
            return retunTime + ":" + sub_ss;
        } catch (Exception e) {
            //  TODO : handle exception
            System.out.println("格式化当前音频时长错误");
        }
        return "00:00:00";
    }

}
